Перейти к основному содержимому

Обработка значения null

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Null

Отсутствие значения

Отсутствие значения — это фундаментальная концепция в программировании, отражающая состояние, когда данные ожидаются, но в текущий момент недоступны. Это состояние не эквивалентно нулю, пустой строке или ложному значению. Отсутствие значения означает: «место для данных зарезервировано, но содержимое отсутствует».

В реальном мире подобные ситуации встречаются постоянно:

  • Поле «дата увольнения» у действующего сотрудника
  • Опциональный параметр в конфигурации приложения
  • Результат поиска, не нашедший совпадений
  • Ссылка на объект, который ещё не создан

Программные языки формализуют это понятие через специальные значения, позволяющие явно выражать отсутствие данных в типизированной системе.

В программировании, для логически пустого значения, нельзя ставить прочерк ("-"), символы, или даже оставлять пустые кавычки. Значит, есть какое-то обозначение отсутствие значения, не так ли?


Null, Undefined, Nothing, None

В реальности мы часто сталкиваемся с отсутствием чего-либо. Но это - тоже информация - факт отсутствия. То есть, если на стуле никто не сидит, то это некое состояние, которое может быть ожидаемым, временным, аномальным или даже критичным. В программировании эту идею формализует понятие null — обозначение отсутствия значения.

null — это явное указание на отсутствие значения. Это не «ничего», а специальное значение, означающее: «здесь должно быть что-то, но сейчас — пусто». Не объект, не число, не строка — это отдельное состояние. Можно явно его присвоить - программист говорит: «я знаю, что здесь пока нет значения».

Пример:

let user = null; // известно, что пользователь не выбран

Чтобы не путать с различными значениями, которые логически - ничего, давайте сделаем таблицу.

ЗначениеЧто на самом деле означает
null«Нет значения»
0«Число ноль»
""«Пустая строка»
false«Ложное значение»
undefined«Не определено» (JS)

Возраст null → неизвестен, а возраст 0 → новорождённый. Это разные смыслы, и их нельзя путать.

undefined — это состояние «ещё не определено». Оно возникает, когда переменная объявлена, но не инициализирована, свойство объекта не существует, функция ничегоне возвращает, или параметр не передан.

Поэтому используйте null, когда намеренно хотите сказать «нет значения», а в JS сравнивайте с ===, чтобы избежать путаницы.

А теперь немного о страшном. В 2009 году Тони Хоар, создатель null в языке ALGOL, назвал это «миллиард-долларовой ошибкой». Почему null так опасен? NullPointerException (NPE) — одна из самых частых ошибок в Java, C#, JavaScript.

null не имеет методов, неявно проникает в логику программы, и если где-то логика должна, к примеру, получать объект, передавать его дальше, использовать его свойства или методы, но вместо объекта ничего не придёт - то будет та самая ошибка NPE. Поэтому нужно всегда выполнять проверку на null - это просто if (что-то == null) то, к примеру, return. Или наоборот, оборачивать нужную логику в if (что-то !== null).

Логически - «делай, только если данные есть». Искусство проверки на null в разных языках и проектах отличается, кто-то выделяет отдельные классы для проверок, кто-то использует nullable-типы (которые могут быть null).


Ключевые слова отсутствия значения

ЯзыкКлючевое словоТип значенияОсобенности
JavaScript / TypeScriptnullОтдельный примитивный типЯвное отсутствие значения
JavaScriptundefinedОтдельный примитивный типНеинициализированная переменная или отсутствующее свойство
PythonNoneСинглтон класса NoneTypeЕдинственное значение отсутствия
JavanullЛитерал без типаПрименим только к ссылочным типам
C#nullЛитерал без типаПрименим к ссылочным типам и nullable-типам
PHPnullСпециальный тип NULLЕдинственное значение типа
SwiftnilЗначение опционального типаТребует явного объявления опциональности
KotlinnullЗначение nullable-типаТребует суффикса ? в объявлении типа
RubynilЭкземпляр класса NilClassЕдинственное значение отсутствия
GonilНулевое значение для указателей, слайсов, мапПрименимо к составным типам
SQLNULLОтсутствие данных в ячейкеСпециальная семантика в запросах

Примеры использования отсутствия значения

JavaScript

// Явное указание отсутствия пользователя
let currentUser = null;

// Неинициализированная переменная
let pendingRequest;
console.log(pendingRequest); // undefined

// Отсутствующее свойство объекта
const profile = { name: "Алексей" };
console.log(profile.age); // undefined

В этом примере:

  • null — программист сознательно указал отсутствие текущего пользователя
  • undefined — переменная объявлена, но не получила значения
  • undefined — свойство age не существует в объекте profile

Python

# Отсутствие результата поиска
def find_user_by_id(user_id):
if user_id in database:
return database[user_id]
return None

user = find_user_by_id(999)
if user is None:
print("Пользователь не найден")

В этом примере:

  • None возвращается как сигнал об отсутствии результата
  • Проверка выполняется оператором is вместо == для корректного сравнения синглтона

Java

public class UserService {
private User currentUser = null;

public User getCurrentUser() {
return currentUser;
}

public void logout() {
currentUser = null;
}
}

В этом примере:

  • null присваивается ссылочной переменной currentUser
  • Метод logout() явно обнуляет ссылку на пользователя
  • Примитивные типы (int, boolean) не могут принимать значение null

C#

string optionalComment = null;
int? nullableAge = null; // Nullable<int>

if (optionalComment == null) {
optionalComment = "Комментарий отсутствует";
}

В этом примере:

  • Строковая переменная может содержать null как допустимое состояние
  • int? — сокращённая запись для Nullable<int>, позволяющая хранить отсутствие числового значения
  • Оператор ?? предоставляет значение по умолчанию при отсутствии данных

PHP

<?php
$middleName = null; // Отчество не указано

function getUserRole($userId) {
if (!isset($users[$userId])) {
return null; // Пользователь не найден
}
return $users[$userId]['role'];
}
?>

В этом примере:

  • null используется для опциональных полей данных
  • Функция возвращает null при отсутствии запрошенного пользователя
  • Проверка выполняется функцией is_null() или оператором ===

Проверки на отсутствие значения

Корректная обработка отсутствия значения предотвращает критические ошибки выполнения. Каждый язык предоставляет специфические механизмы проверки.

JavaScript

// Строгое сравнение
if (value === null) {
// Обработка отсутствия значения
}

// Проверка на отсутствие значения или undefined
if (value == null) {
// Обрабатывает и null, и undefined
}

// Оператор нулевого слияния (ES2020)
const displayName = userName ?? "Гость";

// Опциональная цепочка вызовов
const city = user?.address?.city ?? "Не указано";

Оператор ?? возвращает правый операнд только при null или undefined, в отличие от ||, который срабатывает на любые ложные значения (0, "", false).


Python

# Корректная проверка через оператор is
if user is None:
create_default_user()

# Антипаттерн — неправильная проверка
if user == None: # Работает, но не рекомендуется
pass

# Использование or для значения по умолчанию
name = provided_name or "Аноним"

Проверка через is предпочтительнее, так как None является синглтоном, и сравнение по идентичности быстрее и надёжнее.


Java

// Явная проверка
if (user == null) {
throw new IllegalArgumentException("Пользователь обязателен");
}

// Методы из Objects (Java 7+)
import java.util.Objects;

Objects.requireNonNull(user, "Пользователь обязателен");
String name = Objects.requireNonNullElse(user.getName(), "Без имени");

// Паттерн Optional (Java 8+)
Optional<User> optionalUser = userRepository.findById(userId);
String email = optionalUser
.map(User::getEmail)
.orElse("no-email@example.com");

Класс Optional предоставляет функциональный подход к работе с отсутствием значения, исключая необходимость явных проверок null.


C#

// Классическая проверка
if (order == null) {
return NotFound();
}

// Оператор нулевого слияния
string description = product.Description ?? "Описание отсутствует";

// Оператор условного доступа
int? orderCount = shoppingCart?.Items?.Count;

// Pattern matching (C# 7.0+)
if (response is not null) {
ProcessResponse(response);
}

// Nullable reference types (C# 8.0+)
#nullable enable
string? nullableString = null; // Разрешено
string nonNullableString = "значение"; // Не может быть null
#nullable disable

Система nullable reference types в современном C# позволяет выявлять потенциальные ошибки на этапе компиляции.


PHP

<?php
// Проверка через ===
if ($value === null) {
handleMissingValue();
}

// Функция is_null
if (is_null($value)) {
// Альтернативный способ проверки
}

// Оператор нулевого слияния (PHP 7.0+)
$displayName = $userName ?? 'Гость';

// Функция ??= для присваивания при отсутствии значения
$settings['theme'] ??= 'light';
?>

Оператор ?? в PHP работает аналогично JavaScript, возвращая правый операнд только при null.


Ошибки, связанные с отсутствием значения

Некорректная обработка отсутствия значения приводит к ошибкам времени выполнения. Каждый язык имеет свои специфические исключения.

Java: NullPointerException

User user = null;
String name = user.getName(); // NullPointerException здесь

Исключение возникает при попытке вызвать метод или обратиться к полю через null-ссылку. Это одна из самых распространённых ошибок в Java-приложениях.


C#: NullReferenceException

string text = null;
int length = text.Length; // NullReferenceException здесь

Аналог Java NullPointerException, возникает при разыменовании null-ссылки для доступа к членам объекта.


JavaScript: TypeError

const user = null;
console.log(user.name); // TypeError: Cannot read properties of null

JavaScript генерирует TypeError при попытке доступа к свойству или методу null или undefined.


Python: AttributeError

user = None
name = user.name # AttributeError: 'NoneType' object has no attribute 'name'

Python вызывает AttributeError при обращении к атрибуту объекта None.

PHP: TypeError или предупреждение

<?php
$user = null;
echo $user->name; // Fatal error: Uncaught Error: Call to a member function on null
?>

В современных версиях PHP вызов метода на null приводит к фатальной ошибке.


Nullable типы и современные подходы

Современные языки программирования развивают системы типов для безопасной работы с отсутствием значения на этапе компиляции.

Kotlin: Встроенная поддержка nullable типов

var name: String = "Алексей"
name = null // Ошибка компиляции

var nullableName: String? = "Мария"
nullableName = null // Разрешено

// Безопасный вызов
val length = nullableName?.length

// Оператор Elvis
val displayName = nullableName ?: "Гость"

// Утверждение ненулевого значения
val safeName: String = nullableName!! // Исключение при null

Kotlin разделяет типы на nullable (String?) и non-nullable (String) на уровне системы типов, что исключает большинство ошибок времени выполнения.


Swift: Опциональные типы

var name: String? = "Тимур"
name = nil // Разрешено

// Принудительное извлечение (опасно)
let forced = name!

// Опциональное связывание
if let unwrapped = name {
print(unwrapped.count)
}

// Оператор нулевого слияния
let displayName = name ?? "Гость"

Опциональные типы в Swift требуют явной обработки отсутствия значения перед использованием данных.


C#: Nullable reference types

#nullable enable
string message = null; // Предупреждение компилятора
string? nullableMessage = null; // Корректно

void Process(string input) { // input не может быть null
Console.WriteLine(input.Length);
}

Process(null); // Предупреждение компилятора
#nullable disable

Эта функция (доступна с C# 8.0) добавляет статический анализ для выявления потенциальных null-значений до выполнения программы.


TypeScript: Strict null checks

// tsconfig.json
{
"compilerOptions": {
"strictNullChecks": true
}
}

let value: string = "текст";
value = null; // Ошибка: Type 'null' is not assignable to type 'string'

let nullableValue: string | null = "текст";
nullableValue = null; // Разрешено

TypeScript с включённой опцией strictNullChecks требует явного указания null в типе для допустимости отсутствия значения.


Rust: Перечисление Option

enum Option<T> {
Some(T),
None,
}

fn find_user(id: u32) -> Option<User> {
if database.contains(id) {
Some(database.get(id))
} else {
None
}
}

match find_user(42) {
Some(user) => println!("Найден: {}", user.name),
None => println!("Пользователь не найден"),
}

Rust не имеет значения null. Вместо этого используется перечисление Option, которое явно выражает возможность отсутствия значения через типовую систему.